#include "CodigoDelta.h"


using std::string;
using std::bitset;
using std::cout;
using std::endl;
using namespace convertTypes;


string CodigoDelta::getCodigoUnario(unsigned int x) {
	string retorna = "";
	if (x > 0) {
		for (unsigned int i=1; i<x; i++){
			retorna += "1";
		}
		retorna += "0";
	}
	return (char*)retorna.c_str();
}

string CodigoDelta::getCodigoGamma(unsigned int x) {
	string parteUnaria = "";
	string parteBinaria = "";
	int y = floor(log2(x));
	parteUnaria = getCodigoUnario(y + 1);
	parteBinaria = getCodigoBinario(x - pow(2, y), y); //Devuelve el codigo binario del primer parametro en la cantidad de bits del segundo parametro
	return (parteUnaria + parteBinaria);
}

string CodigoDelta::getCodigoDelta(unsigned int x) {
	string parteGamma = "";
	string parteBinaria = "";
	int y = floor(log2(x));
	parteGamma = getCodigoGamma(y + 1);
	parteBinaria = getCodigoBinario(x - pow(2, y), y); //Devuelve el codigo binario del primer parametro en la cantidad de bits del segundo parametro
	parteGamma.append(parteBinaria.c_str());
	return parteGamma;
}


/*Devuelve el valor binario de un número expresado en la cantidad de bytes pedida
*Faltaria agregar la excepcion cuando el numero necesita mas bytes.
*/
string CodigoDelta::getCodigoBinario(unsigned int numero, unsigned int cantBytes){
	string result = "";
	while(cantBytes > 0){
		if (numero >= pow(2, cantBytes-1)){
			result += "1";
			numero -= pow(2, cantBytes-1);
		}else{
			result += "0";
		}
		cantBytes -= 1;
	}
	return result;
}


/*Devuelve un char* con las distancias comprimidas mediante Delta a partir de un vector*/

string CodigoDelta::getCharArrayComprimido(vector <Data>* v_data){
	unsigned int i, j, k;
	string compressed = "";
	CodigoDelta* delta = new CodigoDelta();
	for (i=0; i < v_data->size(); i++) {
		compressed += delta->getCodigoDelta(v_data->at(i).document);
		compressed += delta->getCodigoDelta(v_data->at(i).frecuency);

		for (j=0; j<v_data->at(i).pos->size(); j++) {
			compressed += (delta->getCodigoDelta(v_data->at(i).pos->at(j)));
		}
	}
	int resto = compressed.length()%8;
	if (resto > 0 ){
		for (int x= 0; x<(8-resto); x++){
			compressed += "0";
		}
	}
	string acumulador=string("");
	for (k=0; k<compressed.length(); k+=8){
		uint8_t int8bits;
		int8bits = stringToInt8(compressed.substr(k,8));
		unsigned char c = int8bits;
		acumulador+=(c);
	}
	delete delta;
	return acumulador;
}


vector <Data>*  CodigoDelta::getDataFromFile(ifstream* fd){
	unsigned int cantDocs;
	vector <Data>* v_data=new vector<Data>();

	fd->read(reinterpret_cast<char*>(&cantDocs), sizeof(unsigned int));
	unsigned char buffer[1];
	fd->read(reinterpret_cast<char*>(&buffer), sizeof(char));
	unsigned int proxBitALeer = 0;
	for (unsigned int i= 0; i<cantDocs; i++){

		Data dat;
		dat.pos = new vector<unsigned int>();
		unsigned int docActual = getNumeroFromCodigoDelta(buffer, &proxBitALeer, fd);
		dat.document = docActual;
		unsigned int frecuenciaDocActual = getNumeroFromCodigoDelta(buffer, &proxBitALeer, fd);
		dat.frecuency = frecuenciaDocActual;
		for (unsigned int j= 0; j<frecuenciaDocActual; j++){
			dat.pos->push_back(getNumeroFromCodigoDelta(buffer,&proxBitALeer, fd));
		}
		v_data->push_back(dat);

	}
	return v_data;
}

unsigned int CodigoDelta::getNumeroFromCodigoUnario(string codigo, unsigned int* proxBitALeer){
	string acumulador = "";
	int retorna = 0;
	for (unsigned int i = (*proxBitALeer); i<codigo.length();i++){
		if(codigo.at(i) == '0'){
			retorna = i+1-(*proxBitALeer);
			*proxBitALeer= (i+1)%8;
			break;
		}
	}
	return retorna;
}

unsigned int CodigoDelta::getNumeroFromCodigoDelta(unsigned char* byteBinario, unsigned int* proxBitALeer, ifstream* fd){
	unsigned int retorna;
	unsigned int cantBitsBinario;
	unsigned int valorByteBinario = (int)(byteBinario[0]);
	unsigned int valorBinario;
	string stringByteEnBinario = getCodigoBinario(valorByteBinario,8);
	string parteBinaria="";

	unsigned int valorGamma = getNumeroFromCodigoGamma(byteBinario, proxBitALeer, fd);
	cantBitsBinario = valorGamma -1;
	stringByteEnBinario = getCodigoBinario((int)(byteBinario[0]),8); // lo actualizo por si en gamma o en unario lei un caracter nuevo

	while (cantBitsBinario>8-(*proxBitALeer)){
		parteBinaria += stringByteEnBinario.substr(*proxBitALeer, 8-(*proxBitALeer));
		cantBitsBinario-= (8-(*proxBitALeer));
		read(proxBitALeer, fd, byteBinario);
		stringByteEnBinario = getCodigoBinario((int)(byteBinario[0]),8);
	}
	parteBinaria += stringByteEnBinario.substr(*proxBitALeer, cantBitsBinario);
	*proxBitALeer += cantBitsBinario;
	valorBinario = getDecimalFromBinario(parteBinaria);
	retorna =  valorBinario + pow(2, valorGamma - 1);
	return retorna;
}

unsigned int CodigoDelta::getNumeroFromCodigoGamma(unsigned char* byteBinario, unsigned int* proxBitALeer, ifstream* fd){
	unsigned int valorByteBinario = (int)(byteBinario[0]);
	unsigned int retorna;
	unsigned int valorBinario;
	unsigned int cantBitsBinario;
	string stringByteEnBinario = getCodigoBinario(valorByteBinario,8);
	string parteBinaria="";
	unsigned valorUnario = getNumeroFromCodigoUnario(stringByteEnBinario, proxBitALeer);
	if ((*proxBitALeer)==0){
		read(proxBitALeer, fd, byteBinario);
		// actualizo el string de bits en binario porque lei uno nuevo
		valorByteBinario = (int)(byteBinario[0]);
		stringByteEnBinario = getCodigoBinario(valorByteBinario,8);
	}
	unsigned int acumulador = 0;
	while (valorUnario==0){
		acumulador += 8-*proxBitALeer;
		read(proxBitALeer, fd, byteBinario);
		valorByteBinario = (int)(byteBinario[0]);
		stringByteEnBinario = getCodigoBinario(valorByteBinario,8);
		valorUnario = getNumeroFromCodigoUnario(stringByteEnBinario, proxBitALeer);
		if ((*proxBitALeer)==0){
			read(proxBitALeer, fd, byteBinario);
		}
	}

	valorUnario += acumulador;
	cantBitsBinario = valorUnario -1;
	while (cantBitsBinario>8-(*proxBitALeer)){
		parteBinaria += stringByteEnBinario.substr(*proxBitALeer, 8-(*proxBitALeer));
		cantBitsBinario-= (8-(*proxBitALeer));
		read(proxBitALeer, fd, byteBinario);
		stringByteEnBinario = getCodigoBinario((int)(*byteBinario),8);
	}
	parteBinaria += stringByteEnBinario.substr(*proxBitALeer, cantBitsBinario);
	*proxBitALeer += cantBitsBinario;
	valorBinario = getDecimalFromBinario(parteBinaria);
	retorna = valorBinario + pow(2, valorUnario - 1);
	return retorna;
}


unsigned int CodigoDelta::getDecimalFromBinario(string binario) {
	int w = 0;
	for (unsigned int i = 0; i < binario.length(); i++) {
		if (binario.at(i) == '1'){
			w += pow(2, binario.length() - 1 - i);

		}
	}
	return w;
}


void CodigoDelta::read(unsigned int* proxBitALeer, ifstream* fd, unsigned char* byteBinario){
	fd->read(reinterpret_cast<char*>(byteBinario), sizeof(char));
	*proxBitALeer = 0;
}

/*
 int getNumeroFromCodigoGamma(string codigo){
 string parteUnaria = leerParteUnaria(codigo);//devuelve los bits leidos hasta que aparecio el primer 0 (incluido)
 codigo = codigo - parteUnaria; //Tengo que sacarle la parte que lei.
 int valorParteUnaria = getNumeroFromCodigoUnario(parteUnaria);
 string parteBinaria = leerNEspacios(codigo, valorParteUnaria - 1);
 codigo = codigo - parteBinaria;
 int valorParteBinaria = getNumeroDecimalFromBinario(parteBinaria);
 return valorParteBinaria + pow(2, valorParteUnaria - 1);

 }

 int getNumeroFromCodigoDelta(string codigo){
 int valorParteGamma = getNumeroFromCodigoGamma(codigo);
 string parteBinaria = leerNEspacios(codigo, valorParteGamma - 1);
 codigo = codigo - parteBinaria;
 int valorParteBinaria = getNumeroDecimalFromBinarioEnNBits(parteBinaria, valorParteGamma - 1);
 return valorParteBinaria + pow(2, valorParteGamma - 1);

 }
*/

 /*



void obtenerValoresForTermino(char *filename) {
	ifstream fichero(filename);
	// verificar la apertura del archivo
	if (fichero.bad()) {
		cout << "Error al tratar de abrir archivo";
		cin.get();
		return 1;
	}
	while (fichero.get() != 0) {

	}

}

*/
